#pragma once
#include <iostream>
#include <vector>
#include <functional>
#include "epoll_intf.hpp"
#include "epoll_conn.hpp"
#include "../common/nocopy.hpp"
#include "../common/mysocket.hpp"
#include "../common/logmessage.hpp"
#include "../common/protocol.hpp"

using namespace std;

using func_t = function<void(shared_ptr<Link>)>;

class TcpServer : public NoCopy
{
    static const int SIZE = 128;
    Socket _listensock;
    uint16_t _port;
    string _ip;
    Epoller _epoller;     // epoll对象
    LinkManager _linkmng; // 链接管理器
    epoll_event _epevents[SIZE];
    func_t _UpperBusiness;

public:
    TcpServer(const string ip, uint16_t port, func_t UpperBusiness)
        : _port(port),
          _ip(ip),
          _linkmng(_epoller),
          _epoller(SIZE),
          _UpperBusiness(UpperBusiness)
    {
    }

    void Init()
    {
        // 设置端口号复用(必须在bind之前)，防止因为TIME_WAIT状态导致的bind error
        int opt = 1;
        setsockopt(_listensock.getsock(), SOL_SOCKET, SO_REUSEPORT | SO_REUSEADDR, &opt, sizeof(opt)); // !!!

        _listensock.Bind(_ip, _port);
        _listensock.Listen();
        // 将listensock上的读事件添加到监视队列，并加入链接管理器
        shared_ptr<Link> link_ptr(new Link(_listensock.getsock(), EVENTIN_ET, _linkmng));
        link_ptr->SetCallBack(bind(&TcpServer::Acceptor, this, placeholders::_1), nullptr, nullptr);
        _linkmng.Connect(link_ptr);
    }

    void Loop()
    {
        while (true)
        {
            int n = _epoller.EpollWait(_epevents, SIZE, -1);
            if (n > 0)
            {
                Dispatcher(n);
            }
            _linkmng.PrintLinks();
            cout << "-----------------------------------------------------------------------" << endl;
        }
    }

private:
    void Dispatcher(int sz)
    {
        for (int i = 0; i < sz; ++i)
        {
            uint32_t event = _epevents[i].events;
            int sockfd = _epevents[i].data.fd;
            auto link_ptr = _linkmng.GetLink(sockfd);
            // 编程技巧：将异常事件转换为读写事件，在读写接口下处理异常，能更集中，更详细的进行处理
            //  if (event & EPOLLERR)
            //      event | EPOLLIN;
            //  if (event & EPOLLHUP)
            //      event | EPOLLIN;
            if (event & EPOLLIN)
                link_ptr->_recv_cb(link_ptr);
            if (event & EPOLLOUT)
                link_ptr->_send_cb(link_ptr);
        }
    }

    void Acceptor(shared_ptr<Link> link_ptr)
    {
        string ip;
        uint16_t port;
        while (true)
        {
            int svcfd = _listensock.Accept(&ip, &port);
            if (svcfd >= 0)
            {
                cout << "get a new link! " << ip << "/" << port << endl;
                shared_ptr<Link> newlink_ptr(new Link(svcfd, EVENTIN_ET, _linkmng, ip, port));
                newlink_ptr->SetCallBack(bind(&TcpServer::Receiver, this, placeholders::_1),
                                         bind(&TcpServer::Sender, this, placeholders::_1),
                                         bind(&TcpServer::Excepter, this, placeholders::_1));
                _linkmng.Connect(newlink_ptr);
            }
            else
            {
                if (errno == EWOULDBLOCK) // 表示底层fd数据没有就绪，读完了可以撤了
                    break;
                else if (errno == EINTR) // 表示accept被信号中断，需要继续处理
                    continue;
                else
                {
                    // 获取新链接失败，不要执行except关闭listensock，不能因为一条链接失败影响服务器后续的运行。
                    LogMessage(ERROR, "Socket::Accept Error! strerror: %s", strerror(errno));
                    break;
                }
            }
        }
    }

    void Receiver(shared_ptr<Link> link_ptr)
    {
        char buffer[1024];
        int fd = link_ptr->_sockfd;
        while (true)
        {
            int sz = recv(fd, &buffer, sizeof(buffer) - 1, 0);
            if (sz > 0)
            {
                buffer[sz] = 0;
                link_ptr->AppendInbuf(buffer);
            }
            else if (sz == 0)
            {
                _linkmng.DisConnect(link_ptr); // 对端关闭连接，服务端也断开连接
                LogMessage(DEBUG, "client quit, service end. close fd%d", fd);
                break;
            }
            else
            {
                if (errno == EWOULDBLOCK) // 读完了，撤
                    break;
                else if (errno == EINTR) // 信号中断，没读完，继续读
                    continue;
                else
                {
                    link_ptr->_except_cb(link_ptr); // 读取发生错误，服务端只能断开连接
                    LogMessage(ERROR, "recv error! fd: %d", fd);
                    break;
                }
            }
        }
        // 执行上层业务逻辑
        if (link_ptr->_sockfd != -1) // 要判断一下连接是否有效，因为中途可能关闭连接
            _UpperBusiness(link_ptr);
    }

    void Sender(shared_ptr<Link> link_ptr)
    {
        int fd = link_ptr->_sockfd;
        string &outbuf = link_ptr->GetOutbuf();
        while (true)
        {
            int n = send(fd, outbuf.c_str(), outbuf.size(), 0);
            if (n > 0)
            {
                outbuf.erase(0, n);
                if (outbuf.empty()) // 写完了，撤
                    break;
            }
            else if (n == 0)
            {
                break; // 写完了，撤
            }
            else
            {
                if (errno == EWOULDBLOCK) // 写满了，先撤，等写事件就绪了继续写完！！！
                    break;
                else if (errno == EINTR) // 信号中断，没写完，继续写
                    continue;
                else
                {
                    link_ptr->_except_cb(link_ptr);
                    LogMessage(ERROR, "send error! fd: %d", fd);
                    break;
                }
            }
        }
        // 写事件不能设为常驻事件（总是就绪），写完了就要将该套接字的写事件监听关闭！！！
        if (link_ptr->_sockfd != -1 && outbuf.empty()) // 要判断一下连接是否有效，因为中途可能关闭连接
            link_ptr->ModEvent(link_ptr->_event & ~EPOLLOUT);
    }

    void Excepter(shared_ptr<Link> link_ptr)
    {
        int fd = link_ptr->_sockfd;
        _linkmng.DisConnect(link_ptr);
    }
};